<script lang="ts" setup>
import type { UploadFile, UploadProps } from 'ant-design-vue';

import { computed, ref, watch } from 'vue';

import { Image, message, Spin, Upload } from 'ant-design-vue';
import jsSHA from 'jssha';

import { getUserBaseApi, QiniuCallback } from '#/api';
import { uuid } from '#/utils';

// Props定义
interface Props {
  value?: UploadFile[];
  maxCount?: number;
  maxSize?: number;
  accept?: string;
  multiple?: boolean;
  disabled?: boolean;
  showUploadList?: boolean | UploadProps['showUploadList'];
  uploadText?: string;
  uploadData?: Record<string, any>;
  uploadHeaders?: Record<string, any>;
  getUploadConfigApi?: string;
}

const props = withDefaults(defineProps<Props>(), {
  value: () => [],
  maxCount: 8,
  maxSize: 2,
  accept: 'image/*',
  multiple: true,
  disabled: false,
  showUploadList: true,
  uploadText: '上传',
  uploadData: () => ({}),
  uploadHeaders: () => ({}),
  getUploadConfigApi: '/api/upload/config',
});

// 定义事件
const emit = defineEmits<{
  change: [info: { file: any; fileList: any }];
  error: [error: Error, file: UploadFile];
  'update:keys': [keys: string[]];
  'update:value': [files: UploadFile[]];
  'upload-success': [keys: string[]];
}>();

// 内部文件列表状态 - 添加明确的类型注解
const fileList = ref<UploadFile[]>([]);

// 图片预览相关状态
const previewVisible = ref<boolean>(false);
const previewImage = ref<string>('');
const previewTitle = ref<string>('');

// 监听外部value变化 - 使用watch实现响应式处理
watch(
  () => props.value,
  (newValue) => {
    // 如果是外部更新（非组件内部update触发），则同步到内部fileList
    if (newValue && Array.isArray(newValue)) {
      // 检查是否为字符串数组（父组件传入的完整图片URL）
      if (newValue.length > 0 && typeof newValue[0] === 'string') {
        // 将URL字符串数组转换为文件对象数组，使用类型断言确保TypeScript正确识别类型
        fileList.value = (newValue as unknown as string[]).map(
          (url: string, index: number) => ({
            uid: `preview_${index}_${Date.now()}`,
            name: url.split('/').pop() || 'preview_image',
            status: 'done',
            url,
            path: url,
          }),
        );
      } else {
        // 普通文件对象数组处理
        fileList.value = [...newValue];
      }
    }
    // 注意：不再在外部传入空值时清空fileList，仅在有明确值时更新
  },
  { immediate: true, deep: true },
);

// 计算是否可以继续上传
const canUpload = computed(() => fileList.value.length < props.maxCount);

// 上传配置类型定义
interface UploadConfig {
  maxsize: number;
  savename: string;
  mimetype: string;
  url: string;
  token: string;
  mode: string;
}

// 计算文件SHA1哈希值
async function getFileSha1(file: File): Promise<string> {
  const shaObj = new jsSHA('SHA-1', 'ARRAYBUFFER');
  shaObj.update(await file.arrayBuffer());
  return shaObj.getHash('HEX');
}

// 根据savename模板生成文件key
function getSaveName(
  file: File,
  sha1: string,
  savenameTemplate: string,
): string {
  const fileSuffix = file.name.split('.').pop()?.toLowerCase() || '';
  const fileName = file.name.slice(
    0,
    Math.max(0, file.name.lastIndexOf('.') || file.name.length),
  );
  const dateObj = new Date();

  const replaceArr: Record<string, string> = {
    '{topic}': 'default',
    '{year}': dateObj.getFullYear().toString(),
    '{mon}': `0${dateObj.getMonth() + 1}`.slice(-2),
    '{day}': dateObj.getDate().toString(),
    '{hour}': dateObj.getHours().toString(),
    '{min}': dateObj.getMinutes().toString(),
    '{sec}': dateObj.getSeconds().toString(),
    '{random}': uuid(),
    '{random32}': Math.random().toString(36).slice(2, 10),
    '{filename}': fileName.slice(0, 100),
    '{suffix}': fileSuffix,
    '{.suffix}': `.${fileSuffix}`,
    '{filesha1}': sha1,
  };

  const replaceKeys = Object.keys(replaceArr).join('|');
  const savename =
    savenameTemplate[0] == '/' ? savenameTemplate.slice(1) : savenameTemplate;

  return savename.replaceAll(new RegExp(replaceKeys, 'gm'), (match) => {
    return replaceArr[match] || match;
  });
}
const siteConfigData = ref();
// 获取上传配置
const getUploadConfig = async (): Promise<UploadConfig> => {
  try {
    const { siteConfig } = await getUserBaseApi();
    siteConfigData.value = siteConfig;
    if (!siteConfig) {
      throw new Error('未获取到上传配置(siteConfig不存在)');
    }

    // 确保siteConfig.upload存在
    if (!siteConfig.upload) {
      throw new Error('未获取到上传配置(siteConfig.upload不存在)');
    }
    // 验证配置中的必要字段
    const requiredFields = ['token', 'maxsize', 'mimetype'];
    const missingFields = requiredFields.filter(
      (field) => !(field in siteConfig.upload),
    );

    if (missingFields.length > 0) {
      console.error('上传配置缺少必要字段:', missingFields, siteConfig.upload);
      throw new Error(
        `未获取到完整的上传配置，缺少: ${missingFields.join(', ')}`,
      );
    }
    return siteConfig.upload;
  } catch (error) {
    console.error('获取上传配置失败:', error);
    throw new Error(
      error instanceof Error ? error.message : '获取上传配置失败，请重试',
    );
  }
};

// 自定义上传方法
const customRequest: UploadProps['customRequest'] = async ({
                                                             file,
                                                             onError,
                                                             onProgress,
                                                             onSuccess,
                                                           }) => {
  try {
    // 1. 获取上传配置
    onProgress?.({ percent: 0 });
    message.loading('正在获取上传配置...');

    const uploadConfig = await getUploadConfig();

    // 2. 检查文件类型是否符合配置
    const allowedTypes = uploadConfig.mimetype.split(',');
    const fileExtension = file.name.split('.').pop()?.toLowerCase() || '';
    if (!allowedTypes.includes(fileExtension)) {
      throw new Error(`不支持的文件类型，仅支持${uploadConfig.mimetype}`);
    }

    // 3. 检查文件大小是否符合配置
    if (file.size > uploadConfig.maxsize) {
      throw new Error(
        `文件大小不能超过${(uploadConfig.maxsize / 1024 / 1024).toFixed(2)}MB`,
      );
    }

    // 4. 计算文件SHA1并生成key
    onProgress?.({ percent: 10 });
    message.loading('正在处理文件...');

    // 计算文件SHA1哈希值
    const sha1 = await getFileSha1(file);

    // 根据savename模板生成文件key
    const fileKey = getSaveName(file, sha1, uploadConfig.savename);

    // 5. 构造上传表单数据
    const formData = new FormData();
    formData.append('file', file);
    formData.append('token', uploadConfig.token);
    formData.append('key', fileKey); // 添加key字段，与旧代码保持一致

    // 生成uuid并作为查询参数添加到URL中（按照旧代码的params方式）
    const fileUuid = uuid();

    // 6. 执行上传
    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (e) => {
      if (e.total > 0) {
        const percent = Math.round((e.loaded / e.total) * 100);
        onProgress?.({ percent });
      }
    });

    xhr.addEventListener('load', async () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        try {
          const response = JSON.parse(xhr.responseText);
          // 调用QiniuCallback接口，并传入上传返回的数据
          try {
            const callbackData = {
              url: response.key,
              sha1,
              name: file.name,
              size: file.size,
              type: file.type,
            };
            await QiniuCallback(callbackData);
          } catch (callbackError) {
            console.error('QiniuCallback调用失败:', callbackError);
          }
          onSuccess?.(response, file);
          // 注意：不再手动维护uploadedKeys数组，而是通过handleChange统一处理
        } catch {
          onError?.(new Error('上传成功但解析响应失败'));
        }
      } else {
        onError?.(new Error(`上传失败: ${xhr.statusText}`));
      }
    });

    xhr.addEventListener('error', () => {
      onError?.(new Error('上传过程中发生错误'));
    });

    // 将uuid作为查询参数添加到URL中
    const urlWithParams = new URL(uploadConfig.url);
    urlWithParams.searchParams.append('uuid', fileUuid);

    xhr.open('POST', urlWithParams.toString());
    xhr.send(formData);

    message.destroy();
  } catch (error) {
    message.destroy();
    message.error(error instanceof Error ? error.message : '上传失败');
    onError?.(error instanceof Error ? error : new Error(String(error)));
  }
};

// 文件上传前校验
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
  // 检查文件类型
  const isImage = /^image\//.test(file.type);
  if (!isImage) {
    emit('error', new Error('请上传图片文件！'), file);
    return Upload.LIST_IGNORE;
  }

  // 检查文件大小 (这里保留基本检查，详细检查在customRequest中进行)
  // const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize;
  // if (!isLtMaxSize) {
  //   emit('error', new Error(`文件大小不能超过${props.maxSize}MB！`), file);
  //   return Upload.LIST_IGNORE;
  // }
  return true;
};

// 处理文件变化
const handleChange: UploadProps['onChange'] = async (info) => {
  const { fileList: newFileList } = info;
  console.log();

  // 确保每个文件对象都有正确的url属性，用于预览
  const updatedFileList = await Promise.all(
    newFileList.map(async (file) => {
      if (
        file.status === 'done' &&
        file.response &&
        file.response.key &&
        !file.url
      ) {
        // 如果文件已上传完成但没有url属性，尝试构建完整url
        try {
          const { siteConfig } = await getUserBaseApi();
          const fullUrl = `${siteConfig.apiUrl}${file.response.key}`;
          return {
            ...file,
            url: fullUrl,
          };
        } catch (error) {
          console.error('构建预览URL失败:', error);
        }
      }
      return file;
    }),
  );

  // 更新内部文件列表状态
  fileList.value = updatedFileList;

  // 直接使用完整的fileList作为update:value的参数，保持类型一致性
  emit('update:value', updatedFileList);
  emit('change', info);

  // 另外提供一个专用的事件来传递URL字符串数组
  const keysOnly = updatedFileList
    .filter(
      (file) => file.status === 'done' && file.response && file.response.key,
    )
    .map((file) => siteConfigData.value?.apiUrl + file.response.key);
  emit('update:keys', keysOnly);

  // 检查是否所有上传任务都已完成
  const isAllCompleted = updatedFileList.every(
    (file) => file.status === 'done' || file.status === 'error',
  );
  const hasSuccessfulUploads = updatedFileList.some(
    (file) => file.status === 'done' && file.response && file.response.key,
  );

  // 如果所有文件都已上传完成且有成功上传的文件，触发upload-success事件
  if (isAllCompleted && hasSuccessfulUploads) {
    // 直接使用当前文件列表中的成功文件作为事件参数
    const successfulKeys = updatedFileList
      .filter(
        (file) => file.status === 'done' && file.response && file.response.key,
      )
      .map((file) => siteConfigData.value?.apiUrl + file.response.key);
    emit('upload-success', successfulKeys);
  }
};

// 处理删除文件 - 使用更简单可靠的逻辑来确保只删除被点击的文件
const handleRemove = (file) => {
  const newFileList = fileList.value.filter((item) => item.uid !== file.uid);
  fileList.value = newFileList;

  // 通知父组件更新
  const keysOnly = newFileList
    .map((f) => {
      if (f.response?.key) return siteConfigData.value?.apiUrl + f.response.key;
      return f.url || f.path;
    })
    .filter(Boolean);
  emit('update:value', keysOnly);
};
</script>

<template>
  <!-- 自定义缩略图显示区域 -->
  <div >
    <div v-if="showUploadList && fileList.length > 0" class="custom-image-list">
      <div
        v-for="file in fileList"
        :key="file.uid || file.key"
        class="custom-image-item"
      >
        <div class="image-wrapper">
          <Image
            v-if="
              file.url ||
              (file.response && file.response.key && siteConfigData.value) ||
              file.path
            "
            :src="
              file.url ||
              file.path ||
              `${siteConfigData.value.apiUrl}${file.response.key}`
            "
            alt="缩略图"
            class="custom-image-thumbnail"
          />
          <div v-if="file.status !== 'uploading'" class="image-overlay">
            <button
              class="delete-button"
              @click.stop="handleRemove(file)"
              title="删除图片"
            >
              ×
            </button>
          </div>
        </div>
        <div class="custom-image-info">
          <div v-if="file.status === 'uploading'" class="uploading-status">
            <Spin size="small" />
            <span>正在上传...</span>
          </div>
          <a v-if="file.status === 'error'" @click.stop>重新上传</a>
        </div>
      </div>
      <!-- 拖拽上传组件 -->
    </div>
    <Upload.Dragger
      :file-list="fileList"
      :before-upload="beforeUpload"
      :custom-request="customRequest"
      @change="handleChange"
      :multiple="multiple"
      :accept="accept"
      :show-upload-list="false"
      :disabled="disabled"
      class="dragger-upload"
    >
      <p class="ant-upload-text">点击或拖拽图片到此区域上传</p>
      <p class="ant-upload-hint">
        支持单个或批量上传，最多可上传 {{ props.maxCount }} 张图片
      </p>
    </Upload.Dragger>
  </div>
</template>

<style scoped>
/* 确保自定义图片列表和上传按钮在同一行 */
.custom-image-list {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 16px;
  margin-right: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 上传按钮样式 */
/* 调整Ant Design Upload组件的样式，确保没有额外间距 */
:deep(.ant-upload) {
  margin: 0 !important;
  padding: 0 !important;
}

.custom-image-item {
  position: relative;
  width: 100px;
  height: 100px;
  border-radius: 4px;
  overflow: hidden;
}

.image-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
}

.custom-image-thumbnail {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.image-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.2s;
  display: flex;
  justify-content: center;
  align-items: center;
}

.custom-image-item:hover .image-overlay {
  opacity: 1;
}

.delete-button {
  background: none;
  border: none;
  color: white;
  font-size: 20px;
  cursor: pointer;
  width: 24px;
  height: 24px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.6);
  transition: background-color 0.2s;
}

.delete-button:hover {
  background-color: rgba(255, 0, 0, 0.7);
}

.custom-image-info {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  padding: 4px;
  background-color: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: 12px;
  text-align: center;
}

.uploading-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.upload-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100px;
  height: 100px;
  border: 1px dashed #d9d9d9;
  border-radius: 4px;
  cursor: pointer;
  background-color: #f5f5f5;
  color: #999;
  transition: all 0.3s;
  margin: 0;
  padding: 0;
}

.upload-button:hover {
  border-color: #1890ff;
  background-color: #e6f7ff;
  color: #1890ff;
}

/* 自定义缩略图列表样式 */
.custom-image-list {
  display: flex;
  flex-wrap: wrap;
  margin-top: 16px;
  gap: 16px;
}

/* 单个缩略图项样式 */
.custom-image-item {
  width: 120px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* 图片容器样式 */
.image-wrapper {
  position: relative;
  width: 100px;
  height: 100px;
}

/* 缩略图图片样式 */
.custom-image-thumbnail {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 4px;
  cursor: pointer;
  border: 1px solid #d9d9d9;
}

/* 图片覆盖层样式 - 仅用于定位按钮 */
.image-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 4px;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  padding: 4px;
  pointer-events: none;
}

/* 删除按钮样式 - 右上角灰色按钮 */
.delete-button {
  width: 20px;
  height: 20px;
  background-color: rgba(150, 150, 150, 0.7);
  border: none;
  border-radius: 2px;
  color: white;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: all 0.2s ease;
  opacity: 0.8;
  pointer-events: auto;
}

.delete-button:hover {
  background-color: #ff4d4f;
  opacity: 1;
  transform: none;
}

/* 缩略图信息区域样式 */
.custom-image-info {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}

/* 缩略图名称样式 */
.custom-image-name {
  font-size: 12px;
  color: rgba(0, 0, 0, 0.65);
  word-break: break-all;
  text-align: center;
  margin-bottom: 4px;
}

.uploading-status {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: #666;
  margin-top: 2px;
}

/* 缩略图操作按钮样式 */
.custom-image-info a {
  font-size: 12px;
  color: #1890ff;
  cursor: pointer;
  margin: 2px 0;
}

.custom-image-info a:hover {
  color: #40a9ff;
  text-decoration: underline;
}

/* 拖拽上传组件样式 */
.dragger-upload {
  width: 100%;
  margin-bottom: 16px;
}

/* 上传按钮样式 */
.ant-upload {
  width: 120px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed #d9d9d9;
  border-radius: 4px;
  background-color: #fafafa;
}
</style>
